package com.xiyuan.smartutils.ids;

import com.xiyuan.smartutils.Asserts;
import com.xiyuan.smartutils.Longs;
import com.xiyuan.smartutils.NetUtils;
import com.xiyuan.smartutils.Systems;
import com.xiyuan.smartutils.constants.SignConstants;
import com.xiyuan.smartutils.enums.LetterCase;
import com.xiyuan.smartutils.exceptions.UtilException;
import com.xiyuan.smartutils.lang.Singleton;
import com.xiyuan.smartutils.seqs.Sequence;

import java.util.UUID;

/**
 * ID编号相关工具类
 *
 * @version v1.4.1 @author lgz 2018-4-20 修改UUID默认为小写，增加指定大小写参数
 */

public final class Ids implements SignConstants {
    
    private static final Sequence SEQUENCE = new Sequence(System.currentTimeMillis(), Longs.getMax(16));
    
    /***************************************************************************************************/
    // 32位通用唯一识别码字符串（Universally Unique Identifier）
    /***************************************************************************************************/
    
    /**
     * 32位通用唯一识别码
     */
    public static String uuid() {
        return UUID.randomUUID().toString().replaceAll("-", _EMPTY_);
    }
    
    /**
     * 32位通用唯一识别码，指定大小写
     */
    public static String uuid(LetterCase letter) {
        return letter.toCase(uuid(), LetterCase.LOWER);
    }
    
    /***************************************************************************************************/
    // 16位唯一长整数，由12位秒级+4位序列号,JS支持最大值16位（9007199254740992），所以16位在JSON中比较合适
    /***************************************************************************************************/
    
    /**
     * 位唯一长整数
     */
    public static long longId() {
        return longId(SEQUENCE);
    }
    
    /**
     * 16位唯一长整数，指定长度为4位数的序列对象
     */
    public static long longId(Sequence sequence) {
        return sequence.next();
    }
    
    /**
     * 获取数据中心ID
     * 数据中心ID依赖于本地网卡MAC地址。
     *
     * @param maxDataCenterId 最大的中心ID
     * @return 数据中心ID
     */
    public static long getDataCenterId(long maxDataCenterId) {
        Asserts.as(maxDataCenterId > 0, "maxDatacenterId must be > 0");
        if (maxDataCenterId == Long.MAX_VALUE) {
            maxDataCenterId -= 1;
        }
        long id = 1L;
        byte[] mac = null;
        try {
            mac = NetUtils.getLocalHardwareAddress();
        }
        catch (UtilException ignore) {
            // ignore
        }
        if (null != mac) {
            id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
            id = id % (maxDataCenterId + 1);
        }
        
        return id;
    }
    
    /**
     * 获取机器ID，使用进程ID配合数据中心ID生成<br>
     * 机器依赖于本进程ID或进程名的Hash值。
     *
     * @param datacenterId 数据中心ID
     * @param maxWorkerId  最大的机器节点ID
     * @return ID
     */
    public static long getWorkerId(long datacenterId, long maxWorkerId) {
        final StringBuilder mpid = new StringBuilder();
        mpid.append(datacenterId);
        try {
            mpid.append(Systems.getPid());
        }
        catch (UtilException igonre) {
            //ignore
        }
        /*
         * MAC + PID 的 hashcode 获取16个低位
         */
        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }
    
    
    /**
     * 创建Twitter的Snowflake 算法生成器。
     * <p>
     * 特别注意：此方法调用后会创建独立的{@link Snowflake}对象，每个独立的对象ID不互斥，会导致ID重复，请自行保证单例！
     * </p>
     * 分布式系统中，有一些需要使用全局唯一ID的场景，有些时候我们希望能使用一种简单一些的ID，并且希望ID能够按照时间有序生成。
     *
     * <p>
     * snowflake的结构如下(每部分用-分开):<br>
     *
     * <pre>
     * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
     * </pre>
     * <p>
     * 第一位为未使用，接下来的41位为毫秒级时间(41位的长度可以使用69年)<br>
     * 然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点）<br>
     * 最后12位是毫秒内的计数（12位的计数顺序号支持每个节点每毫秒产生4096个ID序号）
     *
     * <p>
     * 参考：<a href="http://www.cnblogs.com/relucent/p/4955340.html">http://www.cnblogs.com/relucent/p/4955340.html</a>
     *
     * @param workerId     终端ID
     * @param datacenterId 数据中心ID
     * @return {@link Snowflake}
     * @deprecated 此方法容易产生歧义：多个Snowflake实例产生的ID会产生重复，此对象在单台机器上必须单例，请使用{@link #getSnowflake(long, long)}
     */
    @Deprecated
    public static Snowflake createSnowflake(long workerId, long datacenterId) {
        return new Snowflake(workerId, datacenterId);
    }
    
    /**
     * 获取单例的Twitter的Snowflake 算法生成器对象<br>
     * 分布式系统中，有一些需要使用全局唯一ID的场景，有些时候我们希望能使用一种简单一些的ID，并且希望ID能够按照时间有序生成。
     *
     * <p>
     * snowflake的结构如下(每部分用-分开):<br>
     *
     * <pre>
     * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
     * </pre>
     * <p>
     * 第一位为未使用，接下来的41位为毫秒级时间(41位的长度可以使用69年)<br>
     * 然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点）<br>
     * 最后12位是毫秒内的计数（12位的计数顺序号支持每个节点每毫秒产生4096个ID序号）
     *
     * <p>
     * 参考：<a href="http://www.cnblogs.com/relucent/p/4955340.html">http://www.cnblogs.com/relucent/p/4955340.html</a>
     *
     * @param workerId     终端ID
     * @param datacenterId 数据中心ID
     * @return {@link Snowflake}
     * @since 4.5.9
     */
    public static Snowflake getSnowflake(long workerId, long datacenterId) {
        return Singleton.get(Snowflake.class, workerId, datacenterId);
    }
    
    /**
     * 获取单例的Twitter的Snowflake 算法生成器对象<br>
     * 分布式系统中，有一些需要使用全局唯一ID的场景，有些时候我们希望能使用一种简单一些的ID，并且希望ID能够按照时间有序生成。
     *
     * <p>
     * snowflake的结构如下(每部分用-分开):<br>
     *
     * <pre>
     * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
     * </pre>
     * <p>
     * 第一位为未使用，接下来的41位为毫秒级时间(41位的长度可以使用69年)<br>
     * 然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点）<br>
     * 最后12位是毫秒内的计数（12位的计数顺序号支持每个节点每毫秒产生4096个ID序号）
     *
     * <p>
     * 参考：<a href="http://www.cnblogs.com/relucent/p/4955340.html">http://www.cnblogs.com/relucent/p/4955340.html</a>
     *
     * @param workerId 终端ID
     * @return {@link Snowflake}
     */
    public static Snowflake getSnowflake(long workerId) {
        return Singleton.get(Snowflake.class, workerId);
    }
    
    /**
     * 获取单例的Twitter的Snowflake 算法生成器对象<br>
     * 分布式系统中，有一些需要使用全局唯一ID的场景，有些时候我们希望能使用一种简单一些的ID，并且希望ID能够按照时间有序生成。
     *
     * <p>
     * snowflake的结构如下(每部分用-分开):<br>
     *
     * <pre>
     * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
     * </pre>
     * <p>
     * 第一位为未使用，接下来的41位为毫秒级时间(41位的长度可以使用69年)<br>
     * 然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点）<br>
     * 最后12位是毫秒内的计数（12位的计数顺序号支持每个节点每毫秒产生4096个ID序号）
     *
     * <p>
     * 参考：<a href="http://www.cnblogs.com/relucent/p/4955340.html">http://www.cnblogs.com/relucent/p/4955340.html</a>
     *
     * @return {@link Snowflake}
     */
    public static Snowflake getSnowflake() {
        return Singleton.get(Snowflake.class);
    }
}
